home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
apply.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
74KB
|
2,439 lines
/*
* static char *rcsid_apply_c =
* "$Id: apply.c,v 1.51 1996/07/24 07:30:42 master Exp master $";
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 1994 Mark Wedel
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author can be reached via e-mail to mwedel@pyramid.com
*/
#include <global.h>
#include <living.h>
#include <spells.h>
#include <skills.h>
#if defined(SunArchitecture) && !(OSMajorVersion == 5)
#include <sys/types.h>
#endif
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#ifdef SOUND_EFFECTS
#include <sounds.h>
#endif
#if defined(vax) || defined(ibm032)
size_t strftime(char *, size_t, const char *, const struct tm *);
time_t mktime(struct tm *);
#endif
void draw_find(object *op, object *find) {
new_draw_info_format(NDI_UNIQUE, 0, op, "You find %s in the chest.",
query_name(find));
}
int apply_id_altar(object *money, object *altar)
{
object *id, *pl;
int success=0;
/* Check for MONEY type is a special hack - it prevents 'nothing needs
* identifying' from being printed out more than it needs to be.
*/
if (!check_altar_sacrifice(altar) || money->type!=MONEY) return 0;
pl=get_map_ob(altar->map, altar->x, altar->y);
while (pl!=NULL && pl->type!=PLAYER)
pl=pl->above;
if (pl==NULL) {
LOG(llevError,"Identify Altar: Can't find player!.\n");
return 0;
}
for (id=pl->inv; id; id=id->below) {
if (!QUERY_FLAG(id, FLAG_IDENTIFIED) && !id->invisible &&
need_identify(id)) {
if (check_altar(altar)) {
identify(id);
new_draw_info_format(NDI_UNIQUE, 0, pl,
"You have %s.", long_desc(id));
if (id->msg) {
new_draw_info(NDI_UNIQUE, 0,pl, "The item has a story:");
new_draw_info(NDI_UNIQUE, 0,pl, id->msg);
}
success=1;
/* If no more money, might as well quit now */
if (!check_altar_sacrifice(altar)) break;
}
else {
LOG(llevError,"check_id_altar: Couldn't do sacrifice when we should have been able to\n");
break;
}
}
}
if (!success) new_draw_info(NDI_UNIQUE, 0,pl,"You have nothing that needs identifying");
return 1;
}
int apply_potion(object *op, object *tmp)
{
int got_one=0;
if(op->type!=PLAYER)
return 0; /* This might change */
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE,0,op,"You should pay for it first.");
return 0;
}
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED))
identify(tmp);
if (tmp->attacktype & AT_DEPLETE) { /* Potion of restoration */
object *depl;
archetype *at;
if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
drain_stat(op);
fix_player(op);
decrease_ob(tmp);
return 1;
}
if ((at = find_archetype("depletion"))==NULL) {
LOG(llevError,"Could not find archetype depletion");
return 0;
}
depl = present_arch_in_ob(at, op);
if (depl!=NULL) {
int i;
for (i = 0; i < 7; i++)
if (get_attr_value(&depl->stats, i)) {
new_draw_info(NDI_UNIQUE,0,op, restore_msg[i]);
}
remove_ob(depl);
free_object(depl);
fix_player(op);
}
else
new_draw_info(NDI_UNIQUE,0,op, "You feel a great loss...");
decrease_ob(tmp);
return 1;
}
if(tmp->attacktype&AT_GODPOWER) {
int i;
for(i=1;i<MIN(11,op->level);i++) {
if (QUERY_FLAG(tmp,FLAG_CURSED) || QUERY_FLAG(tmp,FLAG_DAMNED)) {
if (op->contr->levhp[i]!=1) {
op->contr->levhp[i]=1;
break;
}
if (op->contr->levsp[i]!=1) {
op->contr->levsp[i]=1;
break;
}
if (op->contr->levgrace[i]!=1) {
op->contr->levgrace[i]=1;
break;
}
}
else {
if(op->contr->levhp[i]<9) {
op->contr->levhp[i]=9;
break;
}
if(op->contr->levsp[i]<6) {
op->contr->levsp[i]=6;
break;
}
if(op->contr->levgrace[i]<3) {
op->contr->levgrace[i]=3;
break;
}
}
}
/* Just makes checking easier */
if (i<MIN(11, op->level)) got_one=1;
if (!QUERY_FLAG(tmp,FLAG_CURSED) && !QUERY_FLAG(tmp,FLAG_DAMNED)) {
if (got_one) {
fix_player(op);
new_draw_info(NDI_UNIQUE,0,op,"The Gods smile upon you and remake you");
new_draw_info(NDI_UNIQUE,0,op,"a little more in their image.");
new_draw_info(NDI_UNIQUE,0,op,"You feel a little more perfect.");
}
else
new_draw_info(NDI_UNIQUE,0,op,"The potion had no effect - you are already perfect");
}
else { /* cursed potion */
if (got_one) {
fix_player(op);
new_draw_info(NDI_UNIQUE,0,op,"The Gods are angry and punish you.");
}
else
new_draw_info(NDI_UNIQUE,0,op,"You are fortunate that you are so pathetic.");
}
decrease_ob(tmp);
return 1;
}
/* protection/immunity granting potions */
if(tmp->immune||tmp->protected) {
object *force;
#if 0 /* Allow more than 1 force to work at a time */
/* This is copied from change_abil(), should be moved to common func. */
if((force=present_in_ob(FORCE,op))!=NULL)
remove_force(force);
#endif
force=get_archetype("force");
if(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
force->vulnerable = tmp->immune | tmp->protected;
/* Why set force->type to 0? This makes the result permanent */
/* force->type = 0; */
force->stats.food*=10;
} else {
force->immune=tmp->immune;
force->protected=tmp->protected;
}
force->speed_left= -1;
force = insert_ob_in_ob(force,op);
SET_FLAG(force,FLAG_APPLIED);
change_abil(op,force);
decrease_ob(tmp);
return 1;
}
/* A potion that casts a spell. Healing, restore spellpoint (power potion)
* and heroism all fit into this category.
*/
if (tmp->stats.sp) {
if(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
new_draw_info(NDI_UNIQUE,0,op, "Yech! Your lungs are on fire!");
cast_spell(op,tmp, 0, 3, 0, spellPotion,NULL);
} else
cast_spell(op,tmp, 0, tmp->stats.sp, 0, spellPotion,NULL);
decrease_ob(tmp);
fix_player(op);
return 1;
}
/* Only thing left are the stat potions */
if(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
CLEAR_FLAG(tmp, FLAG_APPLIED);
else
SET_FLAG(tmp, FLAG_APPLIED);
if(!change_abil(op,tmp))
new_draw_info(NDI_UNIQUE,0,op,"Nothing happened.");
/* CLEAR_FLAG is so that if the character has other potions
* that were grouped with the one consumed, his
* stat will not be raised by them. fix_player just clears
* up all the stats.
*/
CLEAR_FLAG(tmp, FLAG_APPLIED);
fix_player(op);
decrease_ob(tmp);
return 1;
}
/****************************************************************************
* Weapon improvement code follows
****************************************************************************/
int check_item(object *op,char *item)
{
int count=0;
if (item==NULL) return 0;
op=op->below;
while(op!=NULL) {
if (strcmp(op->arch->name,item)==0){
if (!QUERY_FLAG (op, FLAG_CURSED) && !QUERY_FLAG (op, FLAG_DAMNED)
/* Loophole bug? -FD- */ && !QUERY_FLAG (op, FLAG_UNPAID) )
{
if (op->nrof == 0)/* this is necessary for artifact sacrifices --FD-- */
count++;
else
count += op->nrof;
}
}
op=op->below;
}
return count;
}
void eat_item(object *op,char *item)
{
object *prev;
prev = op;
op=op->below;
while(op!=NULL) {
if (strcmp(op->arch->name,item)==0) {
decrease_ob_nr(op,op->nrof);
op=prev;
}
prev = op;
op=op->below;
}
}
int check_sacrifice(object *op,object *improver)
{
int count;
#ifndef NEW_IMPROVE_WEAPON
if (check_item(op,"food")<5) {
new_draw_info(NDI_UNIQUE,0,op,"The gods want more food.");
return 0;
}
if (check_item(op,"booze")<10) {
new_draw_info(NDI_UNIQUE,0,op,"The gods want more booze.");
return 0;
}
#endif
count = 0;
if (improver->slaying!=NULL) {
count = check_item(op,improver->slaying);
if (count<1) {
char buf[200];
sprintf(buf,"The gods want more %ss",improver->slaying);
new_draw_info(NDI_UNIQUE,0,op,buf);
return 0;
}
#ifndef NEW_IMPROVE_WEAPON
eat_item(op,improver->slaying);
#endif
}
else
count=1;
#ifndef NEW_IMPROVE_WEAPON
eat_item(op,"food");
eat_item(op,"booze");
#endif
return count;
}
int improve_weapon_stat(object *op,object *improver,object *weapon,
signed char *stat,int sacrifice_count,char *statname)
{
#ifndef NEW_IMPROVE_WEAPON
if (sacrifice_count<2)
return 0;
sacrifice_count = isqrt(sacrifice_count/2);
#endif
new_draw_info(NDI_UNIQUE,0,op,"Your sacrifice was accepted.");
*stat += sacrifice_count;
weapon->last_eat++;
new_draw_info_format(NDI_UNIQUE,0,op,
"Weapon's bonus to %s improved by %d",statname,sacrifice_count);
decrease_ob(improver);
/* So it updates the players stats and the window */
fix_player(op);
return 1;
}
/* Types of improvements, hidden in the sp field. */
#define IMPROVE_PREPARE 1
#define IMPROVE_DAMAGE 2
#define IMPROVE_WEIGHT 3
#define IMPROVE_ENCHANT 4
#define IMPROVE_STR 5
#define IMPROVE_DEX 6
#define IMPROVE_CON 7
#define IMPROVE_WIS 8
#define IMPROVE_CHA 9
#define IMPROVE_INT 10
#define IMPROVE_POW 11
#ifndef NEW_IMPROVE_WEAPON
/* build_weapon returns 0 if it was not able to work. */
/* #### We are hiding extra information about the weapon in the level and
last_eat numbers for an object. Hopefully this won't break anything ??
level == max improve last_eat == current improve*/
int improve_weapon(object *op,object *improver,object *weapon)
{
int sacrifice_count;
char *tmp;
if(improver->stats.sp==IMPROVE_PREPARE) {
if (weapon->level!=0) {
new_draw_info(NDI_UNIQUE,0,op,"Weapon already prepared.");
return 0;
}
/* Could check for other things. I'm assuming all things which provide
other benefits are also magic. */
if (weapon->magic!=0) {
new_draw_info(NDI_UNIQUE,0,op,"Cannot prepare magic weapons.");
return 0;
}
sacrifice_count=check_sacrifice(op,improver);
if (sacrifice_count<=0)
return 0;
sacrifice_count = isqrt(sacrifice_count);
weapon->level=sacrifice_count;
new_draw_info(NDI_UNIQUE,0,op,"Your sacrifice was accepted.");
new_draw_info_format(NDI_UNIQUE, 0, op,"Your *%s may be improved %d times.",
weapon->name,sacrifice_count);
tmp = (char *) malloc(strlen(weapon->name)+strlen(op->name) + 4);
sprintf(tmp,"%s's %s",op->name,weapon->name);
weapon->name=tmp;
weapon->nrof=0; /* prevents preparing n weapons in the same
slot at once! */
decrease_ob(improver);
weapon->last_eat=0;
return 1;
}
if (!(weapon->level>0)) {
new_draw_info(NDI_UNIQUE, 0,op,"This weapon has not been prepared.");
return 0;
}
if (weapon->level==weapon->last_eat) {
new_draw_info(NDI_UNIQUE, 0,op,"This weapon cannot be improved any more.");
return 0;
}
/* The skill system hands out wc and dam bonuses to fighters
* more generously than the old system (see fix_player). Thus
* we need to curtail the power of player enchanted weapons.
* I changed this to 1 improvement per "fighter" level/5 -b.t.
* Note: Nothing should break by allowing this ratio to be different or
* using normal level - it is just a matter of play balance.
*/
if (QUERY_FLAG(weapon, FLAG_APPLIED)) {
#ifdef ALLOW_SKILLS
object *wc_obj=NULL;
int level=0;
for(wc_obj=op->inv;wc_obj;wc_obj=wc_obj->below)
if(wc_obj->type==EXPERIENCE&&wc_obj->stats.Str) break;
if(!wc_obj)
LOG(llevError,"Error: Player: %s lacks wc experience object\n",op->name);
else
level=wc_obj->level;
if(weapon->last_eat>((level/5)+5)) {
#else
if(weapon->last_eat>(op->level+5)) {
#endif
new_draw_info(NDI_UNIQUE, 0,op,"Improving the weapon will make it");
new_draw_info(NDI_UNIQUE, 0,op,"powerful to use. Unready it if you");
new_draw_info(NDI_UNIQUE, 0,op,"really want to improve it.");
return 0;
}
}
sacrifice_count = check_sacrifice(op,improver);
if (sacrifice_count<=0)
return 0;
switch (improver->stats.sp) {
case IMPROVE_DAMAGE:
sacrifice_count = isqrt(sacrifice_count);
if ((sacrifice_count+weapon->stats.dam)>70)
sacrifice_count = 70 - weapon->stats.dam;
weapon->stats.dam += sacrifice_count;
weapon->weight += sacrifice_count*2000;
new_draw_info(NDI_UNIQUE, 0,op,"Your sacrifice was accepted.");
new_draw_info_format(NDI_UNIQUE, 0, op,
"Damage is increased by %d, weight by %6.1f kg",
sacrifice_count,(float)sacrifice_count*2.0);
weapon->last_eat++;
decrease_ob(improver);
return 1;
case IMPROVE_WEIGHT:
sacrifice_count = isqrt(sacrifice_count)*750;
if (weapon->weight<sacrifice_count)
weapon->weight=1;
else
weapon->weight-=sacrifice_count;
new_draw_info(NDI_UNIQUE, 0,op,"Your sacrifice was accepted.");
new_draw_info_format(NDI_UNIQUE, 0, op,
"Weapon weight reduced by %6.1f kg to %6.1f kg",
(float)sacrifice_count/1000.0,(float)weapon->weight/1000.0);
weapon->last_eat++;
decrease_ob(improver);
return 1;
case IMPROVE_ENCHANT:
new_draw_info(NDI_UNIQUE, 0,op,"Your sacrifice was accepted.");
weapon->magic++;
weapon->last_eat++;
if (weapon->weight>1) weapon->weight=(weapon->weight*90)/100;
new_draw_info_format(NDI_UNIQUE, 0, op
,"Weapon magic increased to %d, weight lowered to %6.1f kg",
weapon->magic,(float)weapon->weight/1000.0);
decrease_ob(improver);
return 1;
case IMPROVE_STR:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Str),
sacrifice_count,(char *) "strength");
case IMPROVE_DEX:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Dex),
sacrifice_count,(char *) "dexterity");
case IMPROVE_CON:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Con),
sacrifice_count,(char *) "constitution");
case IMPROVE_WIS:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Wis),
sacrifice_count,(char *) "wisdom");
case IMPROVE_CHA:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Cha),
sacrifice_count,(char *) "charisma");
case IMPROVE_INT:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Int),
sacrifice_count,(char *) "intelligence");
case IMPROVE_POW:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Pow),
sacrifice_count,(char *) "power");
default:
new_draw_info(NDI_UNIQUE, 0,op,"Unknown improvement type.");
return 0;
}
}
#else
/* This does the prepare weapon scroll */
int prepare_weapon(object *op, object *improver, object *weapon)
{
int sacrifice_count;
char *tmp;
if (weapon->level!=0) {
new_draw_info(NDI_UNIQUE,0,op,"Weapon already prepared.");
return 0;
}
/* If someone wants to do a weapon that makes them vulnerable,
* let them.
*/
if (weapon->immune || weapon->protected ||
weapon->stats.hp || /* regeneration */
weapon->stats.sp || /* sp regeneration */
weapon->stats.exp || /* speed */
weapon->stats.ac) /* AC - only taifu's I think */
{
new_draw_info(NDI_UNIQUE,0,op,"Cannot prepare magic weapons.");
return 0;
}
sacrifice_count=check_sacrifice(op,improver);
if (sacrifice_count<=0)
return 0;
sacrifice_count = isqrt(sacrifice_count);
weapon->level=sacrifice_count;
new_draw_info(NDI_UNIQUE,0,op,"Your sacrifice was accepted.");
eat_item(op, improver->slaying);
new_draw_info_format(NDI_UNIQUE, 0, op,"Your *%s may be improved %d times.",
weapon->name,sacrifice_count);
tmp = (char *) malloc(strlen(weapon->name)+strlen(op->name) + 4);
sprintf(tmp,"%s's %s",op->name,weapon->name);
weapon->name=tmp; /* this seems to be wrong -Tero */
weapon->nrof=0; /* prevents preparing n weapons in the same
slot at once! */
decrease_ob(improver);
weapon->last_eat=0;
return 1;
}
/* this code is by b.t. (thomas@nomad.astro.psu.edu) -
* only 'enchantment' of armour is possible - improving
* the stats of a player w/ armour as well as a weapon
* will probably horribly unbalance the game. Magic enchanting
* depends on the level of the character - ie the plus
* value (magic) of the armour can never be increased beyond
* the level of the character / 10 -- rounding upish, nor may
* the armour value of the piece of equipment exceed either
* the users level or 99)
*/
int improve_armour(object *op, object *improver, object *armour)
{
int addarm;
addarm = armour->armour/25 + op->level/20 + 1;
if (armour->magic>=(op->level/10+1) || ((armour->armour +
addarm) >= op->level )) {
new_draw_info(NDI_UNIQUE, 0,op,"You are not yet powerfull enough");
new_draw_info(NDI_UNIQUE, 0,op,"to improve this armour.");
return 0;
}
if( (armour->armour + addarm) <= 99) {
armour->magic++;
armour->armour+=addarm;
armour->weight+=armour->weight*0.05;
decrease_ob(improver);
return 1;
} else {
armour->magic++;
new_draw_info(NDI_UNIQUE, 0,op,"The armour value of this equipment");
new_draw_info(NDI_UNIQUE, 0,op,"cannot be further improved.");
decrease_ob(improver);
return 1;
}
}
/* This is the new improve weapon code */
/* build_weapon returns 0 if it was not able to work. */
/* #### We are hiding extra information about the weapon in the level and
last_eat numbers for an object. Hopefully this won't break anything ??
level == max improve last_eat == current improve*/
int improve_weapon(object *op,object *improver,object *weapon)
{
int sacrifice_count, sacrifice_needed=0;
if(improver->stats.sp==IMPROVE_PREPARE) {
return prepare_weapon(op, improver, weapon);
}
if (weapon->level==0) {
new_draw_info(NDI_UNIQUE, 0,op,"This weapon has not been prepared.");
return 0;
}
if (weapon->level==weapon->last_eat) {
new_draw_info(NDI_UNIQUE, 0,op,"This weapon cannot be improved any more.");
return 0;
}
if (QUERY_FLAG(weapon, FLAG_APPLIED)) {
#ifdef ALLOW_SKILLS
object *wc_obj=NULL;
int level=0;
for(wc_obj=op->inv;wc_obj;wc_obj=wc_obj->below)
if(wc_obj->type==EXPERIENCE&&wc_obj->stats.Str) break;
if(!wc_obj)
LOG(llevError,"Error: Player: %s lacks wc experience object\n",op->name);
else
level=wc_obj->level;
if(weapon->last_eat>((level/5)+5)) {
#else
if(weapon->last_eat>(op->level+5)) {
#endif
new_draw_info(NDI_UNIQUE, 0,op,"Improving the weapon will make it too");
new_draw_info(NDI_UNIQUE, 0,op,"powerful for you to use. Unready it if you");
new_draw_info(NDI_UNIQUE, 0,op,"really want to improve it.");
return 0;
}
}
/* This just increases damage by 5 points, no matter what. No sacrifice
* is needed. Since stats.dam is now a 16 bit value and not 8 bit,
* don't put any maximum value on damage - the limit is how much the
* weapon can be improved.
*/
if (improver->stats.sp==IMPROVE_DAMAGE) {
weapon->stats.dam += 5;
weapon->weight += 5000; /* 5 KG's */
new_draw_info_format(NDI_UNIQUE, 0, op,
"Damage has been increased by 5 to %d", weapon->stats.dam);
weapon->last_eat++;
decrease_ob(improver);
return 1;
}
if (improver->stats.sp == IMPROVE_WEIGHT) {
/* Reduce weight by 20% */
weapon->weight = (weapon->weight * 8)/10;
if (weapon->weight < 1) weapon->weight = 1;
new_draw_info_format(NDI_UNIQUE, 0, op,
"Weapon weight reduced to %6.1f kg",
(float)weapon->weight/1000.0);
weapon->last_eat++;
decrease_ob(improver);
return 1;
}
if (improver->stats.sp == IMPROVE_ENCHANT) {
weapon->magic++;
weapon->last_eat++;
new_draw_info_format(NDI_UNIQUE, 0, op
,"Weapon magic increased to %d",weapon->magic);
decrease_ob(improver);
return 1;
}
sacrifice_needed = weapon->stats.Str + weapon->stats.Int + weapon->stats.Dex+
weapon->stats.Pow + weapon->stats.Con + weapon->stats.Cha +
weapon->stats.Wis;
if (sacrifice_needed<1)
sacrifice_needed =1;
sacrifice_needed *=2;
sacrifice_count = check_sacrifice(op,improver);
if (sacrifice_count < sacrifice_needed) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You need at least %d %s", sacrifice_needed, improver->slaying);
return 0;
}
eat_item(op,improver->slaying);
switch (improver->stats.sp) {
case IMPROVE_STR:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Str),
1,(char *) "strength");
case IMPROVE_DEX:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Dex),
1,(char *) "dexterity");
case IMPROVE_CON:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Con),
1,(char *) "constitution");
case IMPROVE_WIS:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Wis),
1,(char *) "wisdom");
case IMPROVE_CHA:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Cha),
1,(char *) "charisma");
case IMPROVE_INT:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Int),
1,(char *) "intelligence");
case IMPROVE_POW:
return improve_weapon_stat(op,improver,weapon,
(signed char *) &(weapon->stats.Pow),
1,(char *) "power");
default:
new_draw_info(NDI_UNIQUE, 0,op,"Unknown improvement type.");
}
LOG(llevError,"improve_weapon: Got to end of function\n");
return 0;
}
#endif
int check_improve_weapon (object *op, object *tmp)
{
object *otmp;
if(op->type!=PLAYER)
return 0;
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
return 0;
}
if (blocks_magic(op->map,op->x,op->y)) {
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of the scroll.");
return 0;
}
if (op->inv==NULL) {
new_draw_info(NDI_UNIQUE, 0,op,"First item in inventory is not a weapon\n");
return 0;
}
for (otmp=op->inv; (otmp!=NULL && otmp->invisible); otmp=otmp->below) {
if (!otmp) {
new_draw_info(NDI_UNIQUE,0,op,"You have no weapon to enchant.\n");
return 0;
}
}
if (otmp->type!=WEAPON) {
new_draw_info(NDI_UNIQUE, 0,op,"First item in inventory is not a weapon\n");
return 0;
}
new_draw_info(NDI_UNIQUE, 0,op,"Applied weapon builder.");
improve_weapon(op,tmp,otmp);
if (op->contr->eric_server > 0)
esrv_send_item(op, tmp);
else
draw_inventory(op);
return 1;
}
/* archetypes don't contain any MONEY_CHANGER object,
* so it this function really used/useful?
* Converters seems to make almost same. -Tero
*/
void money_change(object *op,char *towhat)
{
object *buying;
char buf[MAX_BUF];
buying = get_archetype(towhat);
if (buying==NULL) {
LOG(llevError,"Unable to find archetype %s\n",towhat);
return;
}
buying->nrof=100;
strncpy(buf,query_cost_string(buying,op,F_BUY),MAX_BUF);
if (pay_for_item(buying,op)) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You paid %s for %s.",buf,query_name(buying));
(void) insert_ob_in_ob(buying,op);
} else {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You can't afford %s.",query_name(buying));
free_object(buying);
}
}
/*
* convert_item() returns 1 if anything was converted, otherwise 0
*/
#define CONV_FROM(xyz) xyz->slaying
#define CONV_TO(xyz) xyz->other_arch
#define CONV_NR(xyz) (unsigned char) xyz->stats.sp
#define CONV_NEED(xyz) (unsigned long) xyz->stats.food
int convert_item(object *item, object *converter) {
int nr=0;
object *tmp;
if(item->type==PLAYER||CONV_FROM(converter)!=item->arch->name||
(CONV_NEED(converter)&&CONV_NEED(converter)>item->nrof))
return 0;
if(CONV_NEED(converter)) {
nr=item->nrof/CONV_NEED(converter);
decrease_ob_nr(item,nr*CONV_NEED(converter));
} else {
remove_ob(item);
free_object(item);
}
item=arch_to_object(converter->other_arch);
if(CONV_NR(converter))
item->nrof=CONV_NR(converter);
if(nr)
item->nrof*=nr;
for(tmp=get_map_ob(converter->map,converter->x,converter->y);
tmp!=NULL;
tmp=tmp->above)
if(tmp->type==SHOP_FLOOR)
break;
if(tmp!=NULL)
SET_FLAG(item,FLAG_UNPAID);
item->x=converter->x;
item->y=converter->y;
insert_ob_in_map(item,converter->map);
return 1;
}
/*
* Eneq(@csd.uu.se): Handle apply on containers.
* Moved to own function and added many features [Tero.Haatanen@lut.fi]
*/
/* added the alchemical cauldron to the code -b.t. */
int apply_container (object *op, object *sack)
{
char buf[MAX_BUF];
object *tmp;
if(op->type!=PLAYER)
return 0; /* This might change */
if (sack==NULL || sack->type != CONTAINER) {
LOG (llevError, "apply_container: %s is not container!\n",sack->name);
return 0;
}
op->contr->last_used = NULL;
op->contr->last_used_id = 0;
if (sack->env!=op) {
if (sack->other_arch == NULL || sack->env != NULL) {
new_draw_info(NDI_UNIQUE, 0,op,"You must get it first.");
return 1;
}
/* It's on the ground, the problems begin */
if (op->container != sack) {
/* it's closed OR some player has opened it */
if (QUERY_FLAG(sack, FLAG_APPLIED)) {
for(tmp=get_map_ob(sack->map, sack->x, sack->y);
tmp && tmp->container != sack; tmp=tmp->above);
if (tmp) {
/* some other player have opened it */
new_draw_info_format(NDI_UNIQUE, 0, op,
"%s is already occupied.", query_name(sack));
return 1;
}
}
}
if ( QUERY_FLAG(sack, FLAG_APPLIED)) {
if (op->container == NULL) {
tmp = arch_to_object (sack->other_arch);
/* not good, but insert_ob_in_ob() is too smart */
CLEAR_FLAG (tmp, FLAG_REMOVED);
tmp->x= tmp->y = tmp->ox = tmp->oy = 0;
tmp->map = NULL;
tmp->env = sack;
if (sack->inv)
sack->inv->above = tmp;
tmp->below = sack->inv;
tmp->above = NULL;
sack->inv = tmp;
SET_FLAG (sack, FLAG_WALK_OFF); /* trying force closing it */
SET_FLAG (sack, FLAG_FLY_OFF);
} else {
CLEAR_FLAG (sack, FLAG_WALK_OFF);
CLEAR_FLAG (sack, FLAG_FLY_OFF);
tmp = sack->inv;
if (tmp && tmp->type == CLOSE_CON) {
remove_ob(tmp);
free_object (tmp);
}
}
}
}
if (QUERY_FLAG (sack, FLAG_APPLIED)) {
if (op->container) {
if (op->container != sack) {
tmp = op->container;
apply_container (op, tmp);
sprintf (buf, "You close %s and open ", query_name(tmp));
op->container = sack;
strcat (buf, query_name(sack));
strcat (buf, ".");
} else {
CLEAR_FLAG (sack, FLAG_APPLIED);
op->container = NULL;
sprintf (buf, "You close %s.", query_name(sack));
}
} else {
CLEAR_FLAG (sack, FLAG_APPLIED);
sprintf (buf, "You open %s.", query_name(sack));
SET_FLAG (sack, FLAG_APPLIED);
op->container = sack;
}
} else { /* not applied */
if (sack->slaying) { /* it's locked */
for (tmp=op->inv; tmp; tmp=tmp->below)
if (tmp->type == SPECIAL_KEY &&
tmp->slaying == sack->slaying)
break;
if (tmp) {
sprintf (buf, "You unlock %s with ", query_name(sack));
strcat (buf, query_name(tmp));
strcat (buf, ".");
SET_FLAG (sack, FLAG_APPLIED);
if (sack->env == NULL) { /* if it's on ground,open it also */
new_draw_info (NDI_UNIQUE,0,op, buf);
apply_container (op, sack);
return 1;
}
} else {
sprintf (buf, "You don't have the key to unlock %s.",
query_name(sack));
}
} else {
sprintf (buf, "You readied %s.", query_name(sack));
SET_FLAG (sack, FLAG_APPLIED);
if (sack->env == NULL) { /* if it's on ground,open it also */
new_draw_info (NDI_UNIQUE, 0, op, buf);
apply_container (op, sack);
return 1;
}
}
}
new_draw_info (NDI_UNIQUE, 0, op, buf);
draw_inventory(op);
draw_all_look(op);
return 1;
}
/*
* Eneq(@csd.uu.se): Handle apply on containers.
* Moved to own function and added many features [Tero.Haatanen@lut.fi]
* This version is for client/server mode.
*/
int esrv_apply_container (object *op, object *sack)
{
char buf[MAX_BUF];
object *tmp;
if(op->type!=PLAYER)
return 0; /* This might change */
if (sack==NULL || sack->type != CONTAINER) {
LOG (llevError, "esrv_apply_container: %s is not container!\n",sack->name);
return 0;
}
if (sack->env != op) {
/* It's on the ground, the problems begin */
if (op->container != sack) {
/* it's closed OR some player has opened it */
if (QUERY_FLAG(sack, FLAG_APPLIED)) {
for(tmp=get_map_ob(sack->map, sack->x, sack->y);
tmp && tmp->container != sack; tmp=tmp->above);
if (tmp) {
/* some other player have opened it */
new_draw_info_format(NDI_UNIQUE, 0, op,
"%s is already occupied.", query_name(sack));
return 1;
}
}
}
if ( QUERY_FLAG(sack, FLAG_APPLIED)) {
if (op->container == NULL) {
SET_FLAG (sack, FLAG_WALK_OFF); /* trying force closing it */
SET_FLAG (sack, FLAG_FLY_OFF);
} else {
CLEAR_FLAG (sack, FLAG_WALK_OFF);
CLEAR_FLAG (sack, FLAG_FLY_OFF);
}
}
}
if (QUERY_FLAG (sack, FLAG_APPLIED)) {
if (op->container) {
if (op->container != sack) {
tmp = op->container;
esrv_apply_container (op, tmp);
sprintf (buf, "You close %s and open ", query_name(tmp));
op->container = sack;
strcat (buf, query_name(sack));
strcat (buf, ".");
esrv_send_item (op, sack);
esrv_send_inventory (op, sack);
} else {
CLEAR_FLAG (sack, FLAG_APPLIED);
op->container = NULL;
sprintf (buf, "You close %s.", query_name(sack));
esrv_send_item (op, sack);
}
} else {
CLEAR_FLAG (sack, FLAG_APPLIED);
sprintf (buf, "You open %s.", query_name(sack));
SET_FLAG (sack, FLAG_APPLIED);
op->container = sack;
esrv_send_item (op, sack);
esrv_send_inventory (op, sack);
}
} else { /* not applied */
if (sack->slaying) { /* it's locked */
for (tmp=op->inv; tmp; tmp=tmp->below)
if (tmp->type == SPECIAL_KEY &&
tmp->slaying == sack->slaying)
break;
if (tmp) {
sprintf (buf, "You unlock %s with ", query_name(sack));
strcat (buf, query_name(tmp));
strcat (buf, ".");
SET_FLAG (sack, FLAG_APPLIED);
if (sack->env == NULL) { /* if it's on ground,open it also */
new_draw_info(NDI_UNIQUE, 0, op, buf);
esrv_apply_container (op, sack);
return 1;
} else {
esrv_send_item (op, sack);
}
} else {
sprintf (buf, "You don't have the key to unlock %s.",
query_name(sack));
}
} else {
sprintf (buf, "You readied %s.", query_name(sack));
SET_FLAG (sack, FLAG_APPLIED);
if (sack->env == NULL) { /* if it's on ground,open it also */
esrv_apply_container (op, sack);
return 1;
} else {
esrv_send_item (op, sack);
}
}
}
new_draw_info(NDI_UNIQUE, 0, op, buf);
return 1;
}
/*
* Returns pointer a static string containing gravestone text
*/
char *gravestone_text (object *op)
{
static char buf2[MAX_BUF];
char buf[MAX_BUF];
time_t now = time (NULL);
strcpy (buf2, " R.I.P.\n\n");
if (op->type == PLAYER)
sprintf(buf, "%s the %s\n", op->name, op->contr->title);
else
sprintf(buf, "%s\n", op->name);
strncat (buf2, " ", 20 - strlen(buf) / 2);
strcat (buf2, buf);
if (op->type == PLAYER)
sprintf(buf, "who was in level %d when killed\n", op->level);
else
sprintf(buf, "who was in level %d when died.\n\n", op->level);
strncat (buf2, " ", 20 - strlen(buf) / 2);
strcat (buf2, buf);
if (op->type == PLAYER) {
sprintf(buf, "by %s.\n\n", op->contr->killer);
strncat (buf2, " ", 21 - strlen(buf) / 2);
strcat (buf2, buf);
}
strftime (buf, MAX_BUF, "%b %d %Y\n", localtime(&now));
strncat (buf2, " ", 20 - strlen(buf) / 2);
strcat (buf2, buf);
return buf2;
}
int make_gravestone (object *op, object *grave)
{
char buf[MAX_BUF];
object *tmp, *stone = NULL, *corpse = NULL;
for (tmp=get_map_ob(op->map, op->x, op->y); tmp; tmp=tmp->above)
if (tmp->type == GRAVESTONE)
stone = tmp;
else if (tmp->type == CORPSE)
corpse = tmp;
for (tmp = op->inv; tmp; tmp = tmp->below)
if (tmp->type == GRAVESTONE && !stone)
stone = tmp;
else if (tmp->type == CORPSE && !corpse)
corpse = tmp;
if (! stone) {
new_draw_info(NDI_UNIQUE, 0,op, "You need a gravestone.");
return 1;
} else if (! corpse) {
new_draw_info(NDI_UNIQUE, 0,op, "You need a corpse.");
return 1;
}
/* make a gravestone */
if(stone->name)
free_string(stone->name);
sprintf (buf, "%s's %s", corpse->name, stone->name);
stone->name=add_string(buf);
if (stone->msg)
free_string(stone->msg);
if (corpse->msg)
stone->msg = add_string(corpse->msg);
else
stone->msg = add_string(gravestone_text(corpse));
stone->x = op->x;
stone->y = op->y;
SET_FLAG (stone, FLAG_NO_PICK);
remove_ob (stone);
insert_ob_in_map(stone, op->map);
/* delete grave and corpse */
remove_ob (corpse);
free_object (corpse);
remove_ob (grave);
free_object (grave);
return 1;
}
/* apply returns 0 if it wasn't possible to apply that object */
/* op is the object that is causing object to be applied, tmp is the object
* being applied.
*/
int apply(object *op, object *tmp) {
char buf[MAX_BUF];
int inven;
if(tmp==NULL) {
if(op!=NULL&&op->type==PLAYER)
new_draw_info(NDI_UNIQUE, 0,op,"There is nothing to apply.");
return 0;
}
inven=(tmp->env!=NULL);
if(op->type==PLAYER&&!inven&&QUERY_FLAG(op,FLAG_FLYING)&&
!QUERY_FLAG(tmp,FLAG_FLYING)&&!QUERY_FLAG(tmp,FLAG_FLY_ON)&&
!QUERY_FLAG(op,FLAG_WIZ)) {
new_draw_info(NDI_UNIQUE, 0,op,"But you are floating high above the ground!");
return 0;
}
while(tmp!=NULL&&(!(QUERY_FLAG(tmp,FLAG_WALK_ON)||QUERY_FLAG(tmp,FLAG_FLY_ON))
&&tmp->invisible))
tmp=inven?tmp->below:tmp->above;
if(tmp==NULL)
return 0;
if(QUERY_FLAG(tmp,FLAG_WAS_WIZ)&&!QUERY_FLAG(op,FLAG_WAS_WIZ)&&op->type==PLAYER) {
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_OB_EVAPORATE);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"The object disappears in a puff of smoke!");
new_draw_info(NDI_UNIQUE, 0,op,"It must have been an illusion.");
remove_ob(tmp);
free_object(tmp);
return 1;
}
if(op->type==PLAYER) {
op->contr->last_used=tmp;
op->contr->last_used_id=tmp->count;
}
switch(tmp->type) {
case PLAYERMOVER:
if (tmp->attacktype && (tmp->level || op->type!=PLAYER)) {
if (!tmp->stats.maxsp) tmp->stats.maxsp=2.0;
op->speed_left = -FABS(tmp->stats.maxsp*op->speed/tmp->speed);
fprintf(stderr,"apply, playermove, player speed_left=%f\n", op->speed_left);
}
return 1;
case SPINNER:
if(op->direction) {
op->direction=absdir(op->direction-tmp->stats.sp);
update_turn_face(op);
}
return 1;
case DIRECTOR:
if(op->direction) {
op->direction=tmp->stats.sp;
update_turn_face(op);
}
return 1;
case BUTTON:
case PEDESTAL:
update_button(tmp);
return 1;
case ALTAR:
/* If it is a player that moved on top, just return. A player can't
* be the sacrifice. This prevents double donation problems.
*/
if (op->type==PLAYER) return 0;
if (check_altar (tmp)) {
/* Simple check. Unfortunately, it means you can't cast magic bullet
* with an altar. We call it a Potion - altars are stationary - it
* is up to map designers to use them properly.
*/
if (tmp->stats.sp) {
object *pl=get_map_ob(tmp->map, tmp->x, tmp->y);
while (pl!=NULL && pl->type!=PLAYER)
pl=pl->above;
if (pl==NULL) {
LOG(llevError,"Identify Altar: Can't find player!.\n");
return 0;
}
cast_spell(pl, tmp, 0, tmp->stats.sp, 0, spellPotion, NULL);
new_draw_info_format(NDI_BLACK, 0, op,
"The altar casts %s.", spells[tmp->stats.sp].name);
} else {
tmp->value = 1; /* works only once */
push_button(tmp);
}
}
return 1;
case ARROW:
if(QUERY_FLAG(op, FLAG_ALIVE)&&tmp->speed) {
if(attack_ob(op,tmp)) {
/* There can be cases where a monster 'kills' an arrow. Typically
* happens for things like black puddings that have hitback properties.
*/
if (!QUERY_FLAG(tmp, FLAG_FREED)) {
remove_ob(tmp);
stop_arrow(tmp,op);
}
}
return 1;
}
return 0;
case CONE: /* A cone in the form of a wall */
if(QUERY_FLAG(op, FLAG_ALIVE)&&tmp->speed)
hit_player(op,tmp->stats.dam*20,tmp,tmp->attacktype);
break;
case FBULLET:
case BULLET:
check_fired_arch(tmp);
return 1;
case TRAPDOOR:
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_FALL_HOLE);
#endif
{
int max;
object *ab;
if(!tmp->value) {
int tot;
for(ab=tmp->above,tot=0;ab!=NULL;ab=ab->above)
if(!QUERY_FLAG(ab,FLAG_FLYING))
tot+=ab->weight+ab->carrying;
if(!(tmp->value=(tot>tmp->weight)?1:0))
return 1;
tmp->face=&new_faces[tmp->arch->faces[tmp->value]];
update_object(tmp);
}
for(ab=tmp->above,max=100;--max&&ab&&!QUERY_FLAG(ab, FLAG_FLYING);ab=ab->above) {
transfer_ob(ab,(int)EXIT_X(tmp),(int)EXIT_Y(tmp));
new_draw_info(NDI_UNIQUE, 0,ab,"You fall into a trapdoor!");
}
}
return 1;
case CONVERTER:
if(convert_item(op,tmp))
return 2;
return 0;
case HANDLE:
new_draw_info(NDI_UNIQUE, 0,op,"You turn the handle.");
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_TURN_HANDLE);
#endif
tmp->value=tmp->value?0:1;
tmp->face=&new_faces[tmp->arch->faces[tmp->value]];
update_object(tmp);
push_button(tmp);
return 1;
case TRIGGER:
case TRIGGER_BUTTON:
case TRIGGER_PEDESTAL:
case TRIGGER_ALTAR:
check_trigger(tmp);
return 1;
case DEEP_SWAMP:
deep_swamp(tmp, 1);
return 1;
case CHECK_INV:
check_inv(op, tmp);
break;
case HOLE:
if(tmp->stats.wc>0) /* Is the hole open? */
return 1; /* Nope */
{
int i=find_free_spot(op->arch,op->map,EXIT_X(tmp),EXIT_Y(tmp),0,SIZEOFFREE);
remove_ob(op);
op->x=EXIT_X(tmp)+freearr_x[i],op->y=EXIT_Y(tmp)+freearr_y[i];
insert_ob_in_map(op,op->map);
}
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_FALL_HOLE);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"You fall through the hole!\n");
return 1;
case EXIT:
if(op->type!=PLAYER)
return 0;
if(tmp->head!=NULL)
tmp=tmp->head;
if(!EXIT_PATH(tmp)) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"The %s is closed.",query_name(tmp));
return 1;
}
if (tmp->msg)
new_draw_info(NDI_NAVY, 0, op, tmp->msg);
enter_exit(op,tmp);
break;
case ENCOUNTER:
#ifdef RANDOM_ENCOUNTERS
random_encounter(op, tmp);
#endif
break;
case SHOP_MAT:
{
SET_FLAG(op,FLAG_NO_APPLY);
if(op->type!=PLAYER) {
if(QUERY_FLAG(op, FLAG_UNPAID)) { /* Just move the item to an adjacent place */
int i=find_free_spot(op->arch,op->map,op->x,op->y,1,9);
if(!i) return 1;
transfer_ob(op,op->x+freearr_x[i],op->y+freearr_y[i]);
CLEAR_FLAG(op, FLAG_NO_APPLY);
return 1;
}
if(op->more||op->head) return 1; /* Some nasty bug has to be fixed here... */
teleport(tmp,SHOP_MAT);
CLEAR_FLAG(op, FLAG_NO_APPLY);
return 1;
}
if(get_payment(op)) {
teleport(tmp,SHOP_MAT);
if((tmp=get_map_ob(op->map,op->x,op->y))==NULL||tmp->type!=SHOP_FLOOR)
new_draw_info(NDI_UNIQUE, 0,op,"Thank you for visiting our shop.");
}
else {
int i=find_free_spot(op->arch,op->map,op->x,op->y,1,9);
if(!i)
LOG(llevError,"Internal shop-mat problem.\n");
else {
remove_ob(op);
op->x+=freearr_x[i],op->y+=freearr_y[i];
insert_ob_in_map(op,op->map);
}
}
CLEAR_FLAG(op, FLAG_NO_APPLY);
return 1;
}
case SIGN:
if(tmp->msg==NULL)
new_draw_info(NDI_UNIQUE, 0,op,"Nothing is written on it.");
else
new_draw_info(NDI_UNIQUE | NDI_NAVY,0, op,tmp->msg);
return 1;
case BOOK:
if(op->type!=PLAYER) return 1;
if(QUERY_FLAG(tmp,FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
break;
}
if(tmp->msg==NULL) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You open the %s and find it empty.", tmp->name);
} else
#ifdef ALLOW_SKILLS /* need a literacy skill to read stuff! */
if(!QUERY_FLAG(op,FLAG_WIZ)&&!change_skill(op,SK_LITERACY))
new_draw_info(NDI_UNIQUE, 0,op,
"You are unable to decipher the strange symbols.");
else if(QUERY_FLAG(op,FLAG_WIZ)||((SK_level(op)+5)>=tmp->level))
#endif
{
new_draw_info_format(NDI_UNIQUE, 0, op,
"You open the %s and start reading.", tmp->name);
new_draw_info(NDI_UNIQUE | NDI_NAVY, 0, op,
tmp->msg);
#ifdef ALLOW_SKILLS /* gain xp from reading */
if(!QUERY_FLAG(tmp,FLAG_NO_SKILL_IDENT)) { /* only if not read before */
int exp_gain=calc_skill_exp(op,tmp);
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED)) {
/*exp_gain *= 2; because they just identified it too */
SET_FLAG(tmp,FLAG_IDENTIFIED);
if(tmp->env) draw_inventory(op);
else draw_look(op);
}
add_exp(op,exp_gain);
SET_FLAG(tmp,FLAG_NO_SKILL_IDENT); /* so no more xp gained from this book */
}
} else {
new_draw_info(NDI_UNIQUE, 0,op,"This book is too difficult for you to read.");
#endif
}
return 1;
/* these scrolls allow acquistion of skills by players
* -b.t. thomas@nomad.astro.psu.edu
*/
case SKILLSCROLL:
#ifdef ALLOW_SKILLS /* calls here shouldnt happen, but to be on safe side put here */
if(op->type!=PLAYER)
return 0;
if(QUERY_FLAG(tmp,FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
break;
}
switch((int)learn_skill(op,tmp)) {
case 0:
new_draw_info(NDI_UNIQUE, 0,op,"You already possess the knowledge ");
new_draw_info_format(NDI_UNIQUE, 0,op,"held within the %s.\n",query_name(tmp));
return 1;
case 1:
new_draw_info_format(NDI_UNIQUE, 0,op,"You succeed in learning %s",
skills[tmp->stats.sp].name);
new_draw_info_format(NDI_UNIQUE, 0, op,
"Type 'bind ready_skill %s",skills[tmp->stats.sp].name);
new_draw_info(NDI_UNIQUE, 0,op,"to store the skill in a key.");
fix_player(op); /* to immediately link new skill to exp object */
break;
default:
new_draw_info_format(NDI_UNIQUE,0,op,
"You fail to learn the knowledge of the %s.\n",query_name(tmp));
break;
}
decrease_ob(tmp);
draw_inventory(op);
#endif
return 1;
case SPELLBOOK:
if(op->type!=PLAYER)
return 0;
if(QUERY_FLAG(tmp,FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
break;
}
/* artifact_spellbooks have 'slaying' field point to a spell name,
** instead of having their spell stored in stats.sp. We should update
** stats->sp to point to that spell */
if(tmp->slaying != NULL) {
if((tmp->stats.sp = look_up_spell_name(tmp->slaying)) <0 ){
tmp->stats.sp = -1;
new_draw_info_format(NDI_UNIQUE, 0, op,
"The book's formula for %s is incomplete", tmp->slaying);
return 1;
}
/* now clear tmp->slaying since we no longer need it */
free_string(tmp->slaying);
tmp->slaying=NULL;
}
/* need a literacy skill to learn spells. Also, having a literacy level
* lower than the spell will make learning the spell more difficult */
#ifdef ALLOW_SKILLS
if(op->type==PLAYER&&!change_skill(op,SK_LITERACY)) {
new_draw_info(NDI_UNIQUE, 0,op,"You can't read! Your attempt fails.");
return 1;
}
#endif
if(tmp->stats.sp < 0 || tmp->stats.sp > NROFREALSPELLS
|| spells[tmp->stats.sp].level>(SK_level(op)+10)) {
new_draw_info(NDI_UNIQUE, 0,op,"You are unable to decipher the strange symbols.");
return 1;
}
new_draw_info_format(NDI_UNIQUE, 0, op,
"The spellbook contains the %s level spell %s.",
get_levelnumber(spells[tmp->stats.sp].level),
spells[tmp->stats.sp].name);
if(check_spell_known(op,tmp->stats.sp)) {
new_draw_info(NDI_UNIQUE, 0,op,"You already know that spell.\n");
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
identify(tmp);
if (op->contr->eric_server > 0)
esrv_send_item(op, tmp);
else if (tmp->env)
draw_inventory(op);
else
draw_look(op);
}
return 1;
}
/* I changed spell learning in 2 ways:
*
* 1- MU spells use Int to learn, Cleric spells use Wisdom
*
* 2- The learner's level (in skills sytem level==literacy level; if no
* skills level == overall level) impacts the chances of spell learning.
*
* Overall, chances are the same but a player will find having a high
* literacy rate very useful! -b.t.
*/
if(QUERY_FLAG(tmp,FLAG_STARTEQUIP) || RANDOM()%150-(2*SK_level(op)) <
learn_spell[spells[tmp->stats.sp].cleric ? op->stats.Wis : op->stats.Int]) {
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_LEARN_SPELL);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"You succeed in learning the spell!");
op->contr->known_spells[op->contr->nrofknownspells++]=tmp->stats.sp;
if(op->contr->nrofknownspells == 1)
op->contr->chosen_spell=tmp->stats.sp;
new_draw_info_format(NDI_UNIQUE, 0, op,
"Type 'bind cast %s",spells[tmp->stats.sp].name);
new_draw_info(NDI_UNIQUE, 0,op,"to store the spell in a key.");
#ifdef ALLOW_SKILLS /* xp gain to literacy for spell learning */
if(op->type==PLAYER) add_exp(op,calc_skill_exp(op,tmp));
#endif
} else {
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_FUMBLE_SPELL);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"You fail to learn the spell.\n");
}
remove_ob(tmp);
free_object(tmp);
return 1;
case SCROLL: {
int scroll_spell=tmp->stats.sp, old_spell=0;
rangetype old_shoot=range_none;
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
break;
}
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED))
identify(tmp);
if( scroll_spell < 0 || scroll_spell >= NROFREALSPELLS) {
sprintf(buf, "The scroll just doesn't make sense!");
break;
}
if(op->type==PLAYER) {
#ifdef ALLOW_SKILLS /* players need a literacy skill to read stuff! */
int exp_gain=0;
if(!QUERY_FLAG(op,FLAG_WIZ)&&!change_skill(op,SK_LITERACY)){
new_draw_info(NDI_UNIQUE, 0,op,
"You are unable to decipher the strange symbols.");
break;
}
/* ok, they can read, but did they fumble the formula? */
if(!QUERY_FLAG(op,FLAG_WIZ)&&
RANDOM()%(SK_level(op)+get_weighted_skill_stat_sum(op,SK_LITERACY))<tmp->level)
{
new_draw_info(NDI_UNIQUE,0,op,"You flub the wording of the scroll!");
if(tmp->level<(5+RANDOM()%10)) { /* goofed it */
new_draw_info(NDI_UNIQUE,0,op,"The magic of the scroll is ruined!");
decrease_ob(tmp);
draw_inventory(op);
return 0;
}
#ifdef SPELL_FAILURE_EFFECTS
else if(RANDOM()%3) /* something quite bad */
spell_failure(op,(-5*RANDOM()%(tmp->level+1))-1,spells[scroll_spell].sp);
#endif /* SPELL_FAILURE */
else { /* something bad */
new_draw_info(NDI_UNIQUE,0,op,"The magic of the scroll warps randomly!");
scroll_spell = SP_WOW;
}
}
else if((exp_gain = calc_skill_exp(op,tmp)))
add_exp(op,exp_gain); /* all ok, give the player xp for reading */
/* Now, call here so the right skill is readied -- literacy
* isnt necesarily connected to the exp obj to which the xp
* will go (for kills made by the magic of the scroll)
*/
SET_FLAG(tmp,FLAG_APPLIED);
(void) check_skill_to_apply(op,tmp);
CLEAR_FLAG(tmp,FLAG_APPLIED);
#endif/* ALLOW_SKILLS */
old_shoot= op->contr->shoottype;
old_spell = op->contr->chosen_spell;
op->contr->shoottype=range_scroll;
op->contr->chosen_spell = scroll_spell;
}
new_draw_info_format(NDI_BLACK, 0, op,
"The scroll of %s turns to dust.", spells[tmp->stats.sp].name);
new_draw_info_format(NDI_ALL, 6, op,
"%s reads a scroll of %s.",op->name,spells[tmp->stats.sp].name);
cast_spell(op,tmp,0,scroll_spell,0,spellScroll,NULL);
decrease_ob(tmp);
if(op->type==PLAYER) {
if(op->contr->golem==NULL) {
op->contr->shoottype=old_shoot;
op->contr->chosen_spell = old_spell;
}
draw_stats(op);
}
break;
}
case POTION:
SET_FLAG(tmp,FLAG_APPLIED);
(void) check_skill_to_apply(op,tmp);
CLEAR_FLAG(tmp,FLAG_APPLIED);
return apply_potion(op, tmp);
/* Eneq(@csd.uu.se): Handle apply on containers. */
case CLOSE_CON:
if (op->contr->eric_server > 0)
return esrv_apply_container (op, tmp->env);
else
return apply_container (op, tmp->env);
case CONTAINER:
if (op->contr->eric_server > 0)
return esrv_apply_container (op, tmp);
else
return apply_container (op, tmp);
case TREASURE: {
object *treas;
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
break;
}
/* Nice side effect of new treasure creation method is that the treasure
for the chest is done when the chest is created, and put into the chest
inventory. So that when the chest burns up, the items still exist. Also
prevents people fromt moving chests to more difficult maps to get better
treasure
*/
treas = tmp->inv;
if(treas==NULL) {
new_draw_info(NDI_UNIQUE, 0,op,"The chest was empty.");
decrease_ob(tmp);
return 1;
}
do {
remove_ob(treas);
draw_find(op,treas);
treas->x=op->x,treas->y=op->y;
/* Changed (0.91.2) - always drop treasure to floor (needed for
* trap code
*/
insert_ob_in_map(treas,op->map);
} while ((treas=tmp->inv)!=NULL);
decrease_ob(tmp);
/* Done to re-stack map with player on top? */
remove_ob(op);
insert_ob_in_map(op,op->map);
break;
}
case WAND:
if(apply_special(op,tmp))
return 1;
if(op->type==PLAYER) {
if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
op->contr->shoottype=range_wand;
op->contr->chosen_item_spell= tmp->stats.sp;
if (QUERY_FLAG(tmp,FLAG_BEEN_APPLIED) || QUERY_FLAG(tmp, FLAG_IDENTIFIED))
op->contr->known_spell = 1;
else
op->contr->known_spell = 0;
fix_player(op);
}
} else {
if(QUERY_FLAG(tmp, FLAG_APPLIED))
SET_FLAG(op, FLAG_READY_WAND);
else
CLEAR_FLAG(op, FLAG_READY_WAND);
}
return 1;
case ROD:
if(apply_special(op,tmp))
return 1;
if(op->type==PLAYER) {
if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
op->contr->shoottype=range_rod;
op->contr->chosen_item_spell=tmp->stats.sp;
if (QUERY_FLAG(tmp, FLAG_BEEN_APPLIED) || QUERY_FLAG(tmp, FLAG_IDENTIFIED))
op->contr->known_spell = 1;
else
op->contr->known_spell = 0;
fix_player(op);
}
} else {
if(QUERY_FLAG(tmp, FLAG_APPLIED))
SET_FLAG(op, FLAG_READY_ROD);
else
CLEAR_FLAG(op, FLAG_READY_ROD);
}
return 1;
case HORN:
if(apply_special(op,tmp))
return 1;
if(op->type==PLAYER) {
if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
op->contr->shoottype=range_horn;
op->contr->chosen_item_spell=tmp->stats.sp;
if (QUERY_FLAG(tmp, FLAG_BEEN_APPLIED) || QUERY_FLAG(tmp, FLAG_IDENTIFIED))
op->contr->known_spell = 1;
else
op->contr->known_spell = 0;
fix_player(op);
}
} else {
if(QUERY_FLAG(tmp, FLAG_APPLIED))
SET_FLAG(op, FLAG_READY_HORN);
else
CLEAR_FLAG(op, FLAG_READY_HORN);
}
return 1;
case BOW:
if(apply_special(op,tmp))
return 0;
if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You will now fire %s with %s.",
tmp->race ? tmp->race : "nothing", query_name(tmp));
if(op->type==PLAYER) {
op->contr->shoottype=range_bow;
fix_player(op);
}
}
return 1;
#ifdef ALLOW_SKILLS
case SKILL: /* allows skill tools to be used -b.t */
if(apply_special(op,tmp))
return 0;
if(!tmp->invisible&&QUERY_FLAG(tmp, FLAG_APPLIED)) {
SET_FLAG(op, FLAG_READY_SKILL);
op->chosen_skill=tmp;
if(op->type==PLAYER) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You can now use the skill: %s.",skills[tmp->stats.sp].name);
op->contr->shoottype=range_skill;
link_player_skill(op, tmp);
fix_player(op);
draw_stats(op);
}
} else {
CLEAR_FLAG(op, FLAG_READY_SKILL);
unlink_skill(tmp);
}
return 1;
#endif
case WEAPON:
case ARMOUR:
case BOOTS:
case GLOVES:
case AMULET:
case GIRDLE:
case BRACERS:
case SHIELD:
case HELMET:
case RING:
case CLOAK:
/* Mol(mol@meryl.csd.uu.se) compressed return & apply_s into one statement */
/* Frank: If done this way, a ! must be prepended */
return !apply_special(op,tmp);
case DRINK:
case FOOD:
case FLESH:
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
return 1;
}
if(op->type!=PLAYER)
op->stats.hp=op->stats.maxhp;
else {
if(op->stats.food+tmp->stats.food>999) {
if(tmp->type==FOOD || tmp->type==FLESH)
new_draw_info(NDI_UNIQUE, 0,op,"You can't possibly eat all that now.");
else
new_draw_info(NDI_UNIQUE, 0,op,"You can't possibly drink all that now.");
return 1;
}
if(!QUERY_FLAG(tmp, FLAG_CURSED)) {
char buf[MAX_BUF];
if(tmp->type==DRINK)
sprintf(buf,"Ahhh...that %s tasted good.",tmp->name);
else
sprintf(buf,"The %s tasted %s",tmp->name,
tmp->type==FLESH?"terrible!":"good.");
new_draw_info(NDI_UNIQUE, 0,op,buf);
op->stats.food+=tmp->stats.food;
op->stats.hp+=tmp->stats.food/50;
if(op->stats.hp>op->stats.maxhp)
op->stats.hp=op->stats.maxhp;
}
/* special food hack -b.t. */
if(tmp->title || QUERY_FLAG(tmp,FLAG_CURSED)) eat_special_food(op,tmp);
draw_stats(op);
}
decrease_ob(tmp);
return 1;
case POISON:
if(op->type!=PLAYER)
return 0;
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
return 1;
}
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_DRINK_POISON);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"Yech! That tasted poisonous!");
strcpy(op->contr->killer,"poisonous booze");
op->stats.hp+=tmp->stats.hp;
op->stats.food-=op->stats.food/4;
draw_stats(op);
decrease_ob(tmp);
return 1;
case SAVEBED:
if(op->type!=PLAYER)
return 0;
if(!op->contr->name_changed||!op->stats.exp) {
new_draw_info(NDI_UNIQUE, 0,op,"You don't deserve to save your character yet.");
return 1;
}
if(QUERY_FLAG(op,FLAG_WAS_WIZ)) {
new_draw_info(NDI_UNIQUE, 0,op,"Since you have cheated you can't save.");
return 1;
}
remove_ob(op);
op->direction=0;
op->contr->count_left=0;
new_draw_info_format(NDI_UNIQUE | NDI_ALL, 5, op,
"%s leaves the game.",op->name);
strcpy(op->contr->killer,"left");
check_score(op); /* Always check score */
(void)save_player(op,0);
play_again(op);
op->map->players--;
#if MAP_MAXTIMEOUT
op->map->timeout = MAP_TIMEOUT(op->map);
#endif
return 1;
case ARMOUR_IMPROVER:
if(op->type!=PLAYER)
return 0;
if(QUERY_FLAG(tmp, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,op,"You should pay for it first.");
return 0;
}
if (blocks_magic(op->map,op->x,op->y)) {
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of the scroll.");
return 0;
}
if (op->inv==NULL)
return 0;
if (op->inv->type==ARMOUR
|| op->inv->type==CLOAK
|| op->inv->type==BOOTS || op->inv->type==GLOVES
||op->inv->type==BRACERS || op->inv->type==SHIELD
||op->inv->type==HELMET
) {
new_draw_info(NDI_UNIQUE, 0,op,"Applying armour enchantment.");
improve_armour(op,tmp,op->inv);
if (op->contr->eric_server > 0)
esrv_send_item(op, tmp);
else
draw_inventory(op);
return 1;
}
new_draw_info(NDI_UNIQUE, 0,op,"First item in inventory is not armour!\n");
return 0;
case WEAPON_IMPROVER:
return check_improve_weapon(op, tmp);
case MONEY_CHANGER:
if(op->type!=PLAYER)
return 0;
new_draw_info(NDI_UNIQUE, 0,op,"Applied money changer.");
if (tmp->slaying==NULL) {
LOG(llevError,"Money changer converts to nothing???\n");
/* If this is so critical, we should at least the object and generate
a core file */
/* kill(getpid(),1);*/
return 0;
}
money_change(op,tmp->slaying);
return 1;
case CLOCK: {
time_t t = time(NULL);
strftime(buf, sizeof(buf), "Time is %I:%M %p", localtime(&t));
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_CLOCK);
#endif
new_draw_info(NDI_UNIQUE, 0,op, buf);
return 1;
}
case MENU:
if (op->type!=PLAYER) return 0;
shop_listing(op);
return 1;
case POWER_CRYSTAL:
apply_power_crystal(op,tmp); /* see egoitem.c */
return 1;
case LIGHTER: /* for lighting torches/lanterns/etc */
apply_lighter(op,tmp);
return 1;
case GRAVE:
if (op->type != PLAYER)
return 0;
return make_gravestone (op, tmp);
/* Drop a certain amount of gold, and have one item identified */
case IDENTIFY_ALTAR:
return apply_id_altar(op, tmp);
default:
#if 0
if(op->type==PLAYER) {
new_draw_info(NDI_UNIQUE, 0,op,
"I don't know how to apply the %s.",query_name(tmp));
}
#endif
return 0;
}
return 1;
}
void apply_below(object *op) {
object *tmp;
/* If we are using a container, then lets apply what is in the container
* instead of whats on the ground.
*/
if (op->container!=NULL) {
for(tmp=op->container->inv;tmp!=NULL&&!apply(op,tmp);tmp=tmp->below);
} else
for(tmp=op->below;tmp!=NULL&&!apply(op,tmp);tmp=tmp->below);
}
int apply_special(object *who,object *op) { /* wear/wield */
object *tmp;
char buf[MAX_BUF];
int i;
if(who==NULL) {
LOG(llevError,"apply_special() from object without environment.\n");
return 1;
}
if(op->env!=who) {
new_draw_info(NDI_UNIQUE, 0,who,"You must get it first.");
return 1;
}
if(QUERY_FLAG(op,FLAG_APPLIED)) {
if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED))
{
new_draw_info_format(NDI_UNIQUE, 0, who,
"No matter how hard you try, you just can't\nremove %s.",
query_name(op));
return 1;
}
CLEAR_FLAG(op, FLAG_APPLIED);
switch(op->type) {
case WEAPON:
if(!change_abil(who,op)) {
new_draw_info_format(NDI_UNIQUE, 0, who,
"You can't get rid of the %s.",query_name(op));
SET_FLAG(op, FLAG_APPLIED);
return 1;
}
#ifdef ALLOW_SKILLS /* 'unready' melee weapons skill if it is current skill */
(void) check_skill_to_apply(who,op);
if(QUERY_FLAG(who,FLAG_READY_WEAPON))
CLEAR_FLAG(who,FLAG_READY_WEAPON);
#endif
sprintf(buf,"You unwield %s.",query_name(op));
break;
#ifdef ALLOW_SKILLS
case SKILL: /* allows objects to impart skills */
if(!change_abil(who,op)) {
new_draw_info_format(NDI_UNIQUE, 0, who,
"You can't stop using the %s.",query_name(op));
SET_FLAG(op, FLAG_APPLIED);
return 1;
}
if(who->type==PLAYER) {
new_draw_info_format(NDI_UNIQUE, 0, who,
"You can no longer use the skill: %s.",skills[op->stats.sp].name);
who->contr->shoottype=range_none;
who->contr->last_value = -1;
}
CLEAR_FLAG(who, FLAG_READY_SKILL);
who->chosen_skill=NULL;
/* sprintf(buf,"You stop using the %s.",query_name(op));*/
break;
#endif
case ARMOUR:
case HELMET:
case SHIELD:
case RING:
case BOOTS:
case GLOVES:
case AMULET:
case GIRDLE:
case BRACERS:
case CLOAK:
if(!change_abil(who,op)) {
new_draw_info_format(NDI_UNIQUE, 0, who,
"You can't remove the %s.",query_name(op));
SET_FLAG(op, FLAG_APPLIED);
return 1;
}
sprintf(buf,"You unwear %s.",query_name(op));
break;
case BOW:
case WAND:
case ROD:
case HORN:
#ifdef ALLOW_SKILLS
(void) check_skill_to_apply(who,op);
#endif
sprintf(buf,"You unready %s.",query_name(op));
if(who->type==PLAYER) {
who->contr->shoottype = range_none;
who->contr->last_value = -1;
}
break;
default:
sprintf(buf,"You unapply %s.",query_name(op));
break;
}
new_draw_info(NDI_UNIQUE, 0,who,buf);
tmp=merge_ob(op,NULL);
fix_player(who);
if(who->type==PLAYER)
if (who->contr->eric_server > 0) {
if (tmp) { /* it was merged */
esrv_del_item (who->contr->eric_server, op->count);
op = tmp;
}
esrv_send_item(who, op);
} else
draw_inventory(who);
return 0;
}
if (QUERY_FLAG(op, FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE, 0,who, "You should pay for it first.\n");
return 0;
}
i=0;
for(tmp=who->inv;tmp!=NULL;tmp=tmp->below)
if(tmp->type==op->type&&QUERY_FLAG(tmp, FLAG_APPLIED)&&tmp!=op)
if(tmp->type==RING&&!i)
i=1;
else if(apply_special(who,tmp))
return 0;
if(op->nrof > 1)
tmp = get_split_ob(op,op->nrof - 1);
else
tmp = NULL;
switch(op->type) {
case WEAPON: {
int level=who->level;
if(!QUERY_FLAG(who,FLAG_USE_WEAPON)) {
sprintf(buf,"You can't use %s.",query_name(op));
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
new_draw_info(NDI_UNIQUE, 0,who,buf);
return 0;
}
/* The skill system hands out wc and dam bonuses to fighters
* more generously than the old system (see fix_player). Thus
* we need to curtail the power of player enchanted weapons.
* I changed this to 1 improvement per "fighter" level/5 -b.t.
* Note: Nothing should break by allowing this ratio to be different or
* using normal level - it is just a matter of play balance.
*/
#ifdef ALLOW_SKILLS
if(who->type==PLAYER) {
object *wc_obj=NULL;
for(wc_obj=who->inv;wc_obj;wc_obj=wc_obj->below)
if(wc_obj->type==EXPERIENCE&&wc_obj->stats.Str) break;
if(!wc_obj)
LOG(llevError,"Error: Player: %s lacks wc experience object\n",who->name);
else
level=wc_obj->level;
}
if(op->last_eat>((level/5)+5)) {
#else
if(op->last_eat>(level+5)) { /* peterm: last eat is used by
the prepare weapon scroll
to state how many times a weapon
has been improved. It's sort
of like a weapon level. Setting
a limit here prevents people
from using an enchanted weapon
too unreasonably powerful for them */
#endif
new_draw_info(NDI_UNIQUE, 0,who,
"That weapon is too powerful for you to use.");
new_draw_info(NDI_UNIQUE, 0, who, "It would consume your soul!.");
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
return 1;
}
if( op->level && (strncmp(op->name,who->name,strlen(who->name)))) {
/* if the weapon does not have the name as the character, can't use it. */
/* (Ragnarok's sword attempted to be used by Foo: won't work) */
new_draw_info(NDI_UNIQUE, 0,who,"The weapon does not recognize you as it's owner.");
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
return 1;
}
SET_FLAG(op, FLAG_APPLIED);
#ifdef ALLOW_SKILLS /*check for melee weapons skill, alter player status.
* Note that we need to call this *before* change_abil */
if(!check_skill_to_apply(who,op)) return 0;
if(!QUERY_FLAG(who,FLAG_READY_WEAPON))
SET_FLAG(who, FLAG_READY_WEAPON);
#endif
if(!change_abil(who,op)) {
CLEAR_FLAG(op, FLAG_APPLIED);
sprintf(buf,"You receive a jolt when you try to wield %s.",
query_name(op));
new_draw_info(NDI_UNIQUE, 0,who,buf);
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
return 1;
}
sprintf(buf,"You wield %s.",query_name(op));
break;
}
case ARMOUR:
case HELMET:
case SHIELD:
case BOOTS:
case GLOVES:
case GIRDLE:
case BRACERS:
case CLOAK:
if(!QUERY_FLAG(who, FLAG_USE_ARMOUR)) {
sprintf(buf,"You can't use %s.",query_name(op));
new_draw_info(NDI_UNIQUE, 0,who,buf);
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
return 0;
}
case RING:
case AMULET:
SET_FLAG(op, FLAG_APPLIED);
if(!change_abil(who,op)) {
CLEAR_FLAG(op, FLAG_APPLIED);
sprintf(buf,"You receive a jolt when you try to wear %s.",
query_name(op));
new_draw_info(NDI_UNIQUE, 0,who,buf);
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
return 1;
}
sprintf(buf,"You wear %s.",query_name(op));
break;
#ifdef ALLOW_SKILLS /* this part is needed for skill-tools */
case SKILL:
SET_FLAG(op, FLAG_APPLIED);
if(!change_abil(who,op)) {
CLEAR_FLAG(op, FLAG_APPLIED);
sprintf(buf,"You receive a jolt when you try to use %s.",
query_name(op));
new_draw_info(NDI_UNIQUE, 0,who,buf);
if(tmp!=NULL)
(void) insert_ob_in_ob(tmp,who);
return 1;
}
sprintf(buf,"You ready %s.",query_name(op));
who->chosen_skill=op;
SET_FLAG(who, FLAG_READY_SKILL);
break;
#endif
/* For new skills/exp system- must have 'missile weapons' skill in order to
* weild missile weapons! -b.t.
*/
case BOW:
#ifdef ALLOW_SKILLS /*check for missile weapons skill, alter player status */
SET_FLAG(op, FLAG_APPLIED);
if(!check_skill_to_apply(who,op)) return 0;
#endif
case WAND:
#ifdef ALLOW_SKILLS
SET_FLAG(op, FLAG_APPLIED);
if(!check_skill_to_apply(who,op)) return 0;
#endif
sprintf(buf,"You ready %s.",query_name(op));
break;
case ROD:
case HORN:
#ifdef ALLOW_SKILLS
SET_FLAG(op, FLAG_APPLIED);
if(!check_skill_to_apply(who,op)) return 0;
#endif
default:
sprintf(buf,"You apply %s.",query_name(op));
}
if(!QUERY_FLAG(op, FLAG_APPLIED))
SET_FLAG(op, FLAG_APPLIED);
new_draw_info(NDI_UNIQUE, 0,who,buf);
if(tmp!=NULL)
tmp = insert_ob_in_ob(tmp,who);
fix_player(who);
if(op->type != WAND && who->type == PLAYER)
SET_FLAG(op,FLAG_BEEN_APPLIED);
if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED)) {
if (who->type == PLAYER) {
new_draw_info(NDI_UNIQUE, 0,who, "Oops, it feels deadly cold!");
SET_FLAG(op,FLAG_KNOWN_CURSED);
}
}
if(who->type==PLAYER) {
if (who->contr->eric_server > 0) {
/* if multiple objects were applied, update both slots */
if (tmp)
esrv_send_item(who, tmp);
esrv_send_item(who, op);
} else {
draw_inventory(who);
}
}
return 0;
}
int auto_apply (object *op) {
object *tmp = NULL;
int i;
switch(op->type) {
case SHOP_FLOOR:
if (op->arch->randomitems==0) return 0;
do {
i=10; /* let's give it 10 tries */
while((tmp=generate_treasure(op->arch->randomitems,op->stats.exp?
op->stats.exp:5))==NULL&&--i);
if(tmp==NULL)
return 0;
if(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
{
free_object(tmp);
tmp = NULL;
}
} while(!tmp);
tmp->x=op->x,tmp->y=op->y;
SET_FLAG(tmp,FLAG_UNPAID);
insert_ob_in_map(tmp,op->map);
CLEAR_FLAG(op,FLAG_AUTO_APPLY);
identify(tmp);
break;
case TREASURE:
while ((op->stats.hp--)>0)
create_treasure(op->arch->randomitems, op, GT_ENVIRONMENT,
op->stats.exp ? op->stats.exp :
op->map == NULL ? 14: op->map->difficulty,0);
remove_ob(op);
free_object(op);
break;
}
return tmp ? 1 : 0;
}
/* fix_auto_apply goes through the entire map (only the first time
* when an original map is loaded) and performs special actions for
* certain objects (most initialization of chests and creation of
* treasures and stuff). Calls auto_apply if appropriate.
*/
void fix_auto_apply(mapstruct *m) {
object *tmp,*above=NULL;
int x,y;
for(x=0;x<m->mapx;x++)
for(y=0;y<m->mapy;y++)
for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=above) {
above=tmp->above;
if(QUERY_FLAG(tmp,FLAG_AUTO_APPLY))
auto_apply(tmp);
else if(tmp->type==TREASURE) {
while ((tmp->stats.hp--)>0)
create_treasure(tmp->arch->randomitems, tmp, GT_INVENTORY,
m->difficulty,0);
}
else if(tmp->type==TIMED_GATE) {
tmp->speed = 0;
update_ob_speed(tmp);
}
if(tmp && tmp->arch && tmp->type!=PLAYER && tmp->type!=TREASURE &&
tmp->arch->randomitems)
create_treasure(tmp->arch->randomitems, tmp, GT_INVENTORY,
m->difficulty,0);
}
for(x=0;x<m->mapx;x++)
for(y=0;y<m->mapy;y++)
for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above)
if (tmp->type == TRIGGER || tmp->type == TRIGGER_BUTTON ||
tmp->type == TRIGGER_PEDESTAL || tmp->type == TRIGGER_ALTAR)
check_trigger(tmp);
}
/* eat_special_food() - some food may (temporarily) alter
* player status. We do it w/ this routine and cast_change_attr().
* Note the dircection is set to "99" so that cast_change_attr()
* will only modify the user's status. We shouldnt be able to
* effect others by eating food!
* -b.t.
*/
void eat_special_food(object *who, object *food) {
/* matrix for attacks we can gain protection/immunity too from eating
* be sure to perserve the ordering between at_type[] and sp_type[]! */
static int at_type[] = { AT_PHYSICAL, AT_MAGIC, AT_FIRE, AT_ELECTRICITY,
AT_COLD, AT_DRAIN, AT_POISON, AT_SLOW, AT_PARALYZE, AT_CANCELLATION,
AT_DEPLETE, AT_FEAR };
static int sp_type[] = { SP_ARMOUR, SP_PROT_MAGIC, SP_PROT_FIRE,
SP_PROT_ELEC, SP_PROT_COLD, SP_PROT_DRAIN, SP_PROT_POISON,
SP_PROT_SLOW, SP_PROT_PARALYZE, SP_PROT_CANCEL, SP_PROT_DEPLETE,
SP_HEROISM };
/* matrix for stats we can increase by eating */
int i, stat[] = { STR, DEX, CON, CHA };
int mod_stat[] = {SP_STRENGTH, SP_DEXTERITY, SP_CONSTITUTION, SP_CHARISMA };
/* check if food modifies stats of the eater */
for(i=0; i<(sizeof(stat)/sizeof(int)); i++)
if(get_attr_value(&food->stats, stat[i]))
cast_change_attr(who,99,mod_stat[i]);
/* check if we can protect the eater */
if(food->protected)
for(i=0; i<(sizeof(at_type)/sizeof(int)); i++)
if(food->protected&at_type[i]) cast_change_attr(who,99,sp_type[i]);
/* check for hp, sp change */
if(food->stats.hp!=0)
if(QUERY_FLAG(food, FLAG_CURSED)) {
new_draw_info(NDI_UNIQUE, 0,who,"Eck!...that was poisonous!");
who->stats.hp -= food->stats.hp;
} else {
if(food->stats.hp>0)
new_draw_info(NDI_UNIQUE, 0,who,"You begin to feel better.");
else
new_draw_info(NDI_UNIQUE, 0,who,"Eck!...that was poisonous!");
who->stats.hp += food->stats.hp;
}
if(food->stats.sp!=0)
if(QUERY_FLAG(food, FLAG_CURSED)) {
new_draw_info(NDI_UNIQUE, 0,who,"You are drained of mana!");
who->stats.sp -= food->stats.sp;
if(who->stats.sp<0) who->stats.sp=0;
} else {
new_draw_info(NDI_UNIQUE, 0,who,"You feel a rush of magical energy!");
who->stats.sp += food->stats.sp;
/* place limit on max sp from food? */
}
}
/* apply_lighter() - designed primarily to light torches/lanterns/etc.
* Also burns up burnable material too. First object in the inventory is
* the selected object to "burn". -b.t.
*/
void apply_lighter(object *who, object *lighter) {
object *item = who->inv;
for(item=who->inv;item;item=item->next)
if(!item->invisible&&item!=lighter) break;
if(item) {
new_draw_info_format(NDI_UNIQUE, 0,who,
"You attempt to light the %s with the %s.",item->name,lighter->name);
if(lighter->last_eat && lighter->stats.food) /* lighter is used up */
lighter->stats.food--;
else if(lighter->last_eat) { /* no charges left in lighter */
new_draw_info_format(NDI_UNIQUE, 0,who,"Nothing happens.");
return;
}
save_throw_object(item,AT_FIRE);
} else /* nothing to light */
new_draw_info(NDI_UNIQUE, 0,who,"Nothing happens.");
}